﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace Core.FrameWork.Commons.Extensions
{
    public class ExpressionGenericMapper<TIn, TOut>
    {
        private static Func<TIn, TOut> _FUNC = null;
        private static Func<TIn, TOut, TOut> _FUNC2 = null;
        public static TOut Trans(TIn t)
        {
            return _FUNC(t);
        }

        /// <summary>
        /// 批量实体转换
        /// </summary>
        /// <param name="list">被转换的实体List</param>
        /// <returns></returns>
        public static List<TOut> TransList(List<TIn> list)
        {
            List<TOut> outs = new List<TOut>();
            foreach (var item in list)
            {
                outs.Add(Trans(item));
            }
            return outs;
        }

        /// <summary>
        /// 把TOut 按照TIn来更新
        /// </summary>
        /// <param name="news"></param>
        /// <param name="old"></param>
        /// <returns></returns>
        public static TOut Modify(TIn news, TOut old)
        {
            return _FUNC2(news, old);
        }

        static ExpressionGenericMapper()
        {
            try
            {
                Type stringType = typeof(string);
                #region 创建构造对象的委托
                {
                    ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "m");
                    List<MemberBinding> memberBindingList = new List<MemberBinding>();
                    foreach (var item in typeof(TOut).GetProperties())
                    {
                        var type = typeof(TIn);
                        if (item.PropertyType.IsClass && item.PropertyType != stringType)//排除String以外的引用类型，不进行赋值
                        {
                            continue;
                        }
                        var prop = type.GetProperty(item.GetMappingName());//从Out类找映射的字段名
                        if (prop != null)//如果TIn里能找到该属性
                        {

                            MemberExpression property = Expression.Property(parameterExpression, prop);
                            MemberBinding memberBinding = Expression.Bind(item, property);
                            memberBindingList.Add(memberBinding);
                        }
                    }
                    foreach (var item in typeof(TOut).GetFields())
                    {
                        var type = typeof(TIn);
                        if (item.FieldType.IsClass && item.FieldType != stringType)//排除String以外的引用类型，不进行赋值
                        {
                            continue;
                        }
                        var prop = type.GetField(item.GetMappingName());//从Out类找映射的字段名
                        if (prop != null)
                        {
                            MemberExpression property = Expression.Field(parameterExpression, prop);
                            MemberBinding memberBinding = Expression.Bind(item, property);
                            memberBindingList.Add(memberBinding);
                        }

                    }
                    MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
                    Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
                    {
                    parameterExpression
                    });
                    _FUNC = lambda.Compile();//拼装是一次性的
                }
                #endregion
                #region 修改对象的委托
                {




                    ParameterExpression oExpression = Expression.Parameter(typeof(TOut), "o");
                    ParameterExpression nExpression = Expression.Parameter(typeof(TIn), "n");
                    List<MemberBinding> memberBindingList = new List<MemberBinding>();
                    foreach (var item in typeof(TOut).GetProperties())
                    {
                        var type = typeof(TIn);
                        if (item.PropertyType.IsClass && item.PropertyType != stringType)//排除String以外的引用类型，不进行赋值
                        {
                            continue;
                        }
                        var prop = type.GetProperty(item.GetMappingName());
                        if (prop != null)
                        {
                            MemberExpression property = Expression.Property(nExpression, prop);
                            MemberBinding memberBinding = Expression.Bind(item, property);
                            memberBindingList.Add(memberBinding);
                        }
                        else
                        {
                            var propOld = typeof(TOut).GetProperty(item.Name);
                            MemberExpression property = Expression.Property(oExpression, propOld);
                            MemberBinding memberBinding = Expression.Bind(item, property);
                            memberBindingList.Add(memberBinding);
                        }
                    }
                    foreach (var item in typeof(TOut).GetFields())
                    {
                        var type = typeof(TIn);
                        if (item.FieldType.IsClass && item.FieldType != stringType)//排除String以外的引用类型，不进行赋值
                        {
                            continue;
                        }
                        var prop = type.GetField(item.GetMappingName());
                        if (prop != null)
                        {
                            MemberExpression property = Expression.Field(nExpression, prop);
                            MemberBinding memberBinding = Expression.Bind(item, property);
                            memberBindingList.Add(memberBinding);
                        }
                        else
                        {
                            var propOld = typeof(TOut).GetField(item.Name);
                            MemberExpression property = Expression.Field(oExpression, propOld);
                            MemberBinding memberBinding = Expression.Bind(item, property);
                            memberBindingList.Add(memberBinding);
                        }
                    }
                    MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
                    Expression<Func<TIn, TOut, TOut>> lambda = Expression.Lambda<Func<TIn, TOut, TOut>>(memberInitExpression, new ParameterExpression[]
                    {
                   nExpression, oExpression
                    });
                    _FUNC2 = lambda.Compile();//拼装是一次性的
                }
                #endregion
            }
            catch (Exception ex)
            {

                throw;
            }

        }
    }
    public static class ExpressionGenericMapperExtend
    {
        public static string GetMappingName(this PropertyInfo prop)
        {

            if (prop.IsDefined(typeof(MappingAttribute), true))
            {
                MappingAttribute display = prop.GetCustomAttribute(typeof(MappingAttribute)) as MappingAttribute;
                return display.MappingName;
            }
            return prop.Name;

        }

        public static string GetMappingName(this FieldInfo prop)
        {

            if (prop.IsDefined(typeof(MappingAttribute), true))
            {
                MappingAttribute display = prop.GetCustomAttribute(typeof(MappingAttribute)) as MappingAttribute;
                return display.MappingName;
            }
            return prop.Name;

        }
    }
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public class MappingAttribute : Attribute
    {
        public string MappingName;
        public MappingAttribute(string _MappingName)
        {
            this.MappingName = _MappingName;
        }
    }
}
